package net.lovesrc.allin;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.lovesrc.allin.biz.NotificationService;
import net.lovesrc.support.service.JdbcTemplateSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public abstract class BaseBean extends JdbcTemplateSupport implements EnvironmentAware, ApplicationContextAware {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(512);

    private static final Map<String, List<Object>> runningIdsMap = Maps.newConcurrentMap();

    private Environment environment;

    private ApplicationContext applicationContext;

    @Autowired
    protected NotificationService notificationService;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public <T> T getBean(Class<T> clz) {
        return applicationContext.getBean(clz);
    }

    public <T> Collection<T> getBeans(Class<T> clz) {
        return applicationContext.getBeansOfType(clz).values();
    }

    public <T> T tryGetProperties(Class<T> clz, T defValue, String... keys) {
        for (String key : keys) {
            if (environment.containsProperty(key)) {
                return environment.getProperty(key, clz);
            }
        }
        return defValue;
    }

    public void safeRun(Runnable task) {
        try {
            task.run();
        } catch (Exception e) {
            logger.error("safe run task error", e);
        }
    }

    public void execute(Runnable task) {
        EXECUTOR_SERVICE.execute(() -> {
            try {
                task.run();
            } catch (Exception e) {
                logger.error("Execute Error", e);
            }
        });
    }

    public void execute(String key, Object id, Runnable task) {
        Integer maxIngs = tryGetProperties(int.class, 0, key + "_MAX_INGS");
        if (maxIngs == 0) {
            return;
        }
        List<Object> runningIds = runningIdsMap.computeIfAbsent(key, (k) -> Lists.newCopyOnWriteArrayList());
        if (runningIds.size() >= maxIngs || runningIds.contains(id)) {
            return;
        }
        runningIdsMap.get(key).add(id);
        execute(() -> {
            try {
                task.run();
            } catch (Exception e) {
                logger.error("execute error", e);
            } finally {
                runningIdsMap.get(key).remove(id);
            }
        });
    }


}
